﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BitsOfStuff.Support;

namespace BitsOfStuff
{
    /// <summary>
    /// Legasy class for aproximation of curved, now we use Douglas... algoritm
    /// </summary>
    public class BasicCurvedAnalysis
    {
        private MyPoint[] _myPoints;
        //private readonly double _tolerance;

        /// <summary>
        /// Function for curve aproximation based on distance of point from cyrcle around start and end point
        /// </summary>
        /// <param name="intervalBitMap">bitmap of corner points for fraction of curve to subcurves</param>
        /// <param name="myPoints">Raw points of shape</param>
        /// <returns>bitmapp of curves points</returns>
        public bool[] selectExtremePoints(bool[] intervalBitMap, MyPoint[] myPoints)
        {
            _myPoints = myPoints;
            var result = new bool[intervalBitMap.Length];
            for (int i = 0; i < intervalBitMap.Length; i++)
            {
                if ((intervalBitMap[i]))
                {
                    int startIndex = i;
                    i++;
                    while (i < intervalBitMap.Length && !intervalBitMap[i])
                    {
                        i++;
                    }
                    int endIndex = i;
                    var localTolerance = (int)((endIndex - startIndex) * MyParameters.curvedTolerance);
                    if ((endIndex - startIndex) > localTolerance)
                        result = SupportFunctions.simpleBiteMapMarge(result,
                                                            selectExtremePoint(startIndex, endIndex));
                    i--;
                }
            }
            return result;
        }

        private bool[] selectExtremePoint(int startIndex, int endIndex)
        {
            var extremePoints = new List<int>();

            bool[] resultX = selectExtremePointAxe(startIndex,
                                                    endIndex,
                                                    ref extremePoints,
                                                    pointF => pointF.getX());
            bool[] resultY = selectExtremePointAxe(startIndex,
                                                    endIndex,
                                                    ref extremePoints,
                                                    pointF => pointF.getY());

            //bool[] oddCurveResult = new bool[resultX.Length];
            bool[] oddCurveResult = oddCurveAnalysis(extremePoints, MyParameters.curvedMinimalFractionToOddAnalysis);

            bool[] resultXY = SupportFunctions.simpleBiteMapMarge(resultX, resultY);

            return SupportFunctions.simpleBiteMapMarge(oddCurveResult, resultXY);
        }

        private bool[] selectExtremePointAxe(
                    int startIndex,
                    int endIndex,
                    ref List<int> extremePoints,
                    Func<MyPoint, double> myPointGetter)
        {
            var result = new bool[_myPoints.Length];
            var pointValue = new double[endIndex - startIndex];
            for (int i = startIndex; i < endIndex; i++)
            {
                pointValue[i - startIndex] = myPointGetter(_myPoints[i]);
            }
            int maxValueIndex = selectIndexOfMax(pointValue) + startIndex;
            int minValueIndex = selectIndexOfMin(pointValue) + startIndex;
            var localTolerance = (int)((endIndex - startIndex) * MyParameters.curvedTolerance);
            bool maxPass = distanceTest(maxValueIndex, startIndex, endIndex, localTolerance);
            bool minPass = distanceTest(minValueIndex, startIndex, endIndex, localTolerance);

            if (maxPass)
            {
                result[maxValueIndex] = true;
                extremePoints.Add(maxValueIndex);
            }

            if (minPass)
            {
                result[minValueIndex] = true;
                extremePoints.Add(minValueIndex);
            }

            return result;
        }

        private bool[] oddCurveAnalysis(IEnumerable<int> extremePointIndexes, double fraction)
        {
            List<int> uniqExtremePoints = extremePointIndexes.Distinct().ToList();
            uniqExtremePoints.Sort();

            for (int i = 0; i < uniqExtremePoints.Count - 1; i++)
            {
                int distance = uniqExtremePoints[i + 1] - uniqExtremePoints[i];
                if (distance > (_myPoints.Length * fraction))
                {
                    bool[] firstHalf = selectExtremePoint(uniqExtremePoints[i], uniqExtremePoints[i] + distance / 2);
                    bool[] secondHalf = selectExtremePoint(uniqExtremePoints[i + 1] - distance / 2,
                                                           uniqExtremePoints[i + 1]);

                    bool[] result = SupportFunctions.simpleBiteMapMarge(firstHalf, secondHalf);
                    result[uniqExtremePoints[i + 1] - distance / 2] = true;
                    return result;
                }
            }
            return new bool[_myPoints.Length];
        }

        private bool distanceTest(int index, int startIndex, int endIndex, int tolerance)
        {
            if ((Math.Abs(index - startIndex) > tolerance) ||
                Math.Abs(index - endIndex) > tolerance)
                return true;
            return false;
        }

        private int selectIndexOfMax(double[] doubles)
        {
            try
            {
                double max = doubles[0];
                int k = 0;
                int maxPointIndex = (from num in doubles
                                     let index = k++
                                     where num > max
                                     let x = Interlocked.Exchange(ref max, num)
                                     select index).LastOrDefault();
                return maxPointIndex;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private int selectIndexOfMin(double[] doubles)
        {
            try
            {
                double min = doubles[0];
                int k = 0;
                int maxPointIndex = (from num in doubles
                                     let index = k++
                                     where num < min
                                     let x = Interlocked.Exchange(ref min, num)
                                     select index).LastOrDefault();
                return maxPointIndex;
            }
            catch (Exception)
            {
                return 0;
            }
        }
    }
}